package netnotifier;

import java.util.ArrayList;

import com.sun.jna.Callback;
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.Structure;
import com.sun.jna.ptr.PointerByReference;

import netnotifier.notification.RawPacket;

public class NetSniffer implements Runnable
{
	private Thread sniffingThread=null;	
	private NetNotifier notifier=NetNotifier.instance;
	private Pointer pcapT=null; // pcap device descriptor
	private int datalink=-1; // describes what types of frames we get
	
	private boolean closing=false;
	
	// JNA declarations
	public interface PcapLib extends Library
	{
		PcapLib instance = (PcapLib) Native.loadLibrary("pcap",PcapLib.class);

		// JNA mappings
		String pcap_lookupdev(String errbuf);
		Pointer pcap_open_live(String device, int snaplen, int promisc, char[] errBuf);
		int pcap_loop(Pointer pcapT, int count,PcapCallback callback, String user);
		void pcap_close(Pointer pcapT);
		int pcap_datalink(Pointer pcapT);
		int pcap_findalldevs(PointerByReference ifListRef, char[] errBuf);
	}
	
	public interface PcapCallback extends Callback
	{
		public void callback(Pointer useless, PcapPktHdr header, Pointer packet);
	}

	// pcap_pkthdr: included in the callback to tell size of the packet
	public static class PcapPktHdr extends Structure
	{
		// timestamp
		public int seconds;
		public int millis;

		public int capLength;
		public int totalLength;
	}
	
	public static class PcapInterface extends Structure
	{
		PcapInterface next;
		String name;
		String description;
		Pointer addresses; // I could dig deeper here, but I don't want to
		int flags;
	}
	// end of JNA declarations  
	
	
	// the constructor
	public NetSniffer(String deviceName) throws Exception
	{
		char[] errBuf=new char[256];
		pcapT = PcapLib.instance.pcap_open_live(new String(deviceName),65535,0,errBuf);
		datalink=PcapLib.instance.pcap_datalink(pcapT);
		System.out.println("datalink: "+datalink);
		
		sniffingThread=new Thread(this);
		sniffingThread.start();
	}

	public static String[] getDeviceList()
	{
		char[] errBuf=new char[256];
		PointerByReference pbr=new PointerByReference();
		PcapLib.instance.pcap_findalldevs(pbr, errBuf);	
		Pointer pointer=pbr.getValue();
		
		ArrayList<String> deviceList=new ArrayList<String>();
		
		while(pointer!=null)
		{
			Pointer[] pcapIF=pointer.getPointerArray(0,4);
			deviceList.add(pcapIF[1].getString(0));
			pointer=pcapIF[0];
		}
		
		return deviceList.toArray(new String[]{});
	}
	
	public synchronized void stop()
	{
		PcapLib.instance.pcap_close(pcapT);
	}
	
	// run the sniffing thread
	public void run()
	{
		PcapCallback callback=new PcapCallback()
		{
			public void callback(Pointer useless, final PcapPktHdr header, final Pointer packet)
			{
				if(closing);
				notifier.contribute(new RawPacket(header.seconds,datalink,packet.getByteArray(0,header.capLength)));
			}
		};

		PcapLib.instance.pcap_loop(pcapT,0,callback,null);
	}
}